﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace BBCode.Template
{
    public static class MemoryCache
    {
        private const string cacheKeyFormat = "{0}${1}";

        private static readonly ICacheProvider cache;

        static MemoryCache()
        {
            cache = new CacheProvider();
        }

        /// <summary>
        /// Gets the specified id.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="id">The id.</param>
        /// <returns></returns>
        public static T Get<T>(int id)
        {
            return (T) cache.Get(GetCacheKey<T>(id));
        }

        /// <summary>
        /// Gets the specified key.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        public static T Get<T>(string key)
        {
            return (T) cache.Get(GetCacheKey<T>(key));
        }

        /// <summary>
        /// Gets all.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static List<T> GetAll<T>()
        {
            var keyName = GetCacheKey<T>();

            return (from DictionaryEntry item in cache let itemKey = ExtractTypeKey(item.Key.ToString()) where itemKey == keyName select (T) item.Value).ToList();
        }

        /// <summary>
        /// Gets the range.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="idRange">The id range.</param>
        /// <returns></returns>
        public static List<T> GetRange<T>(int[] idRange)
        {
            return (from id in idRange select cache.Get(GetCacheKey<T>(id)) into cachedItem where cachedItem != null select (T) cachedItem).ToList();
        }

        /// <summary>
        /// Saves the specified id.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="id">The id.</param>
        /// <param name="item">The item.</param>
        public static void Save<T>(int id, object item)
        {
            cache.Insert(GetCacheKey<T>(id), item);
        }

        /// <summary>
        /// Saves the specified key.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">The key.</param>
        /// <param name="item">The item.</param>
        public static void Save<T>(string key, object item)
        {
            cache.Insert(GetCacheKey<T>(key), item);
        }

        //public static void SaveAll<T>(List<T> items) where T : IDataObject
        //{
        //    string typeName = typeof(T).Name;
        //    foreach (T item in items)
        //    {
        //        string objectKey = GetCacheKey(typeName, item.ID);
        //        cache.Insert(objectKey, (object)item);
        //    }
        //}

        /// <summary>
        /// Deletes the specified id.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="id">The id.</param>
        public static void Delete<T>(int id)
        {
            cache.Remove(GetCacheKey<T>(id));
        }

        /// <summary>
        /// Deletes all.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public static void DeleteAll<T>()
        {
            var keyName = GetCacheKey<T>();
            foreach (DictionaryEntry item in from DictionaryEntry item in cache let itemKey = ExtractTypeKey(item.Key.ToString()) where itemKey == keyName select item)
            {
                cache.Remove(item.Key.ToString());
            }
        }

        /// <summary>
        /// Clears this instance.
        /// </summary>
        public static void Clear()
        {
            foreach (DictionaryEntry item in cache)
                cache.Remove((string) item.Key);
        }

        /// <summary>
        /// Gets the cache key.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        private static string GetCacheKey<T>()
        {
            var objectType = typeof (T);

            var typeName = objectType.Name;
            return typeName;
        }

        /// <summary>
        /// Gets the cache key.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="id">The id.</param>
        /// <returns></returns>
        private static string GetCacheKey<T>(int id)
        {
            return GetCacheKey(GetCacheKey<T>(), id);
        }

        /// <summary>
        /// Gets the cache key.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        private static string GetCacheKey<T>(string key)
        {
            return GetCacheKey(GetCacheKey<T>(), key);
        }

        /// <summary>
        /// Gets the cache key.
        /// </summary>
        /// <param name="typeName">Name of the type.</param>
        /// <param name="id">The id.</param>
        /// <returns></returns>
        private static string GetCacheKey(string typeName, int id)
        {
            return string.Format(cacheKeyFormat, typeName, id);
        }

        /// <summary>
        /// Gets the cache key.
        /// </summary>
        /// <param name="typeName">Name of the type.</param>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        private static string GetCacheKey(string typeName, string key)
        {
            return string.Format(cacheKeyFormat, typeName, key);
        }

        /// <summary>
        /// Extracts the type key.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        private static string ExtractTypeKey(string key)
        {
            return key.Split('$')[0];
        }
    }
}